package com.taotao.job.rpc.server.hold;

import com.google.common.base.Preconditions;
import com.taotao.job.rpc.core.meta.IfaceMeta;
import com.taotao.job.rpc.core.meta.TaotaoRpcException;
import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.Method;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author fanbeibei
 * @date 2020/11/22 17:02
 */
@Slf4j
public class ProviderHolder {
    private static volatile ProviderHolder instance;

    private ProviderHolder() {

    }

    public static ProviderHolder getInstance() {
        if (null == instance) {
            synchronized (ProviderHolder.class) {
                if (null == instance) {
                    instance = new ProviderHolder();
                }
            }
        }

        return instance;
    }


    /**
     * IfaceMeta -> Method
     */
    protected ConcurrentHashMap<IfaceMeta, Method> methodCache = new ConcurrentHashMap<>();
    /**
     * IfaceMeta -> provider
     */
    private ConcurrentHashMap<IfaceMeta, Object> providerCache = new ConcurrentHashMap<>();


    public Object getProvider(IfaceMeta ifaceMeta) {
        Preconditions.checkArgument(null != ifaceMeta);
        return providerCache.get(ifaceMeta);
    }

    public Method getMethod(IfaceMeta ifaceMeta) {
        Preconditions.checkArgument(null != ifaceMeta);
        return methodCache.get(ifaceMeta);
    }


    public void registerProvider(Object provider, Class<?> ifaceClass, Method method) {
        Preconditions.checkArgument(null != provider);
        Preconditions.checkArgument(null != ifaceClass);
        Preconditions.checkArgument(null != method);

        if (!hasMethod(provider.getClass(), method)) {
            throw new IllegalArgumentException("provider " + provider.getClass().getName() + " has no such method " + method);
        }

        synchronized (this) {
            IfaceMeta ifaceMeta = new IfaceMeta(ifaceClass.getName(), method.getName());
            //把类本身与method的映射关系缓存
            Object old = methodCache.get(ifaceMeta);
            if (null != old) {
                throw new TaotaoRpcException("repeate ifaceClass implements ");
            }

            methodCache.put(ifaceMeta, method);
            providerCache.put(ifaceMeta, provider);


            log.info("ifaceMeta={}", ifaceMeta);
        }


    }

    private boolean hasMethod(Class<?> clazz, Method method) {
        Preconditions.checkArgument(null != clazz);
        Preconditions.checkArgument(null != method);

        try {
            clazz.getMethod(method.getName(), method.getParameterTypes());
            return true;
        } catch (NoSuchMethodException e) {
            return false;
        }
    }

}
